namespace EyeOpen.Linq
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel.DataAnnotations;
	using System.Linq;
	using System.Reflection;

	public static class TypeExtensions
	{
		public static T Cast<T>(this object obj)
		{
			return (T)obj;
		}

		public static bool IsPrimitive(this Type type)
		{
			if (type.IsPrimitive)
			{
				return true;
			}

			if ((type == typeof(Guid)) || (type == typeof(string)) || (type == typeof(decimal)))
			{
				return true;
			}

			return false;
		}

		public static bool IsEnumerable(this Type type)
		{
			if (type.IsPrimitive())
			{
				return false;
			}

			if (type.IsGenericType)
			{
				if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
				{
					return true;
				}
			}

			var isEnumerable =
				type
					.GetInterfaces()
					.Any(x => x.IsGenericType && (x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
			
			return isEnumerable;
		}

		public static IDictionary<PropertyInfo, object[]> GetDataAnnotationsAttributes(this Type type)
		{
			var metadataAttribute =
				type
					.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
					.SingleOrDefault();

			var annotationsObjectType = type;

			if (metadataAttribute != null)
			{
				annotationsObjectType =
					metadataAttribute
					.Cast<MetadataTypeAttribute>()
					.MetadataClassType;
			}

			var properties =
				annotationsObjectType
					.GetPublicProperties();

			properties =
				properties
				.Where(x => x.PropertyType.IsPrimitive())
				.ToList();

			return
				properties
					.ToDictionary(x => x, y => y.GetCustomAttributes(false));
		}

		public static IEnumerable<PropertyInfo> GetReferenceProperties(this Type type)
		{
			return
				type
					.GetPublicProperties()
					.Where(x => !IsEnumerable(x.PropertyType) && !IsPrimitive(x.PropertyType))
					.ToList();
		}

		public static IEnumerable<PropertyInfo> GetCollectionProperties(this Type type)
		{
			return
				type
					.GetPublicProperties()
					.Where(x => IsEnumerable(x.PropertyType))
					.ToList();
		}

		public static IEnumerable<PropertyInfo> GetNotPrimitiveProperties(this Type type)
		{
			return
				type
					.GetPublicProperties()
					.Where(x => !x.PropertyType.IsPrimitive());
		}

		public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
		{
			return
				type
					.GetProperties(BindingFlags.Public | BindingFlags.Instance);
		}
	}
}